using System;
using System.Collections.Generic;
using System.Threading;
using Cysharp.Threading.Tasks;
using IQIGame.Onigao.Framework;
using UnityEngine.Playables;
using UnityEngine.Serialization;

namespace UnityEngine.Timeline
{
    public static class TimelineUtil
    {
        public static UniTask PlayAsync(this PlayableDirector playableDirector, bool unscaleTime = false, bool startWithCurrent = false,
            CancellationToken cancellation = default)
        {
            AutoResetUniTaskCompletionSource tcs = AutoResetUniTaskCompletionSource.Create();
            playableDirector.time = 0;
            playableDirector.stopped += (pd) =>
            {
                // if (pd.time >= pd.duration)
                // {
                if (tcs != null)
                {
                    // 注意！这里要先置空，再调用，TrySetResult会先调用await之后的代码，如果await之后有给TaskSource赋新值，然后这里再置空，那么_completeActTaskSource就永远为null了。
                    // 若置空不及时导致了多次source.TrySetResult，会导致池内数据异常
                    var rTcs = tcs;
                    tcs = null;
                    rTcs.TrySetResult();
                }
                // }
            };

            playableDirector.Play();
            if (cancellation != default)
            {
                cancellation.Register(
                    () =>
                    {
                        if (tcs != null)
                        {
                            // 注意！这里要先置空，再调用，TrySetResult会先调用await之后的代码，如果await之后有给TaskSource赋新值，然后这里再置空，那么_completeActTaskSource就永远为null了。
                            // 若置空不及时导致了多次source.TrySetResult，会导致池内数据异常
                            var rTcs = tcs;
                            tcs = null;
                            rTcs.TrySetCanceled();
                        }
                    });
            }
            return tcs.Task;
        }

        public static async UniTask PlayBack(this PlayableDirector playableDirector, bool unscaleTime = false, bool startWithCurrent = false, CancellationToken cancellation = default)
        {
            playableDirector.initialTime = 1;
            var duration = playableDirector.duration;
            if (!startWithCurrent)
            {
                playableDirector.time = 0;
            }
            playableDirector.Evaluate();
            try
            {
                await UniTask.Yield(cancellation);

                while (playableDirector.time < duration)
                {
                    var deltaTime = unscaleTime ? Time.unscaledDeltaTime : Time.deltaTime;
                    playableDirector.time = playableDirector.time + deltaTime;
                    if (playableDirector.time > duration)
                    {
                        playableDirector.time = duration;
                    }

                    playableDirector.Evaluate();
                    await UniTask.Yield(cancellation);
                }
            }
            finally
            {
                playableDirector.Stop();
            }
        }

        private static List<TimelineContext> paramPool = new List<TimelineContext>();

        public static TimelineContext GetParam()
        {
            if (paramPool.Count > 0)
            {
                var rParam = paramPool[^1];
                paramPool.RemoveAt(paramPool.Count - 1);
                return rParam;
            }
            var rValue = new TimelineContext();
            rValue.Reset();
            return rValue;
        }

        public static void PutToPool(this TimelineContext context)
        {
            if (paramPool.Count < 16)
            {
                context.Reset();
                paramPool.Add(context);
            }
        }

        public static async UniTask PlayAsyncWithParam(this PlayableDirector playableDirector, TimelineContext context)
        {
            if (context == null)
            {
                return;
            }
            playableDirector.initialTime = 1;
            var duration = playableDirector.duration;
            if (!context.startWithCurrent)
            {
                context.time = 0;
            }
            playableDirector.time = context.time;
            playableDirector.Evaluate();
            try
            {
                await UniTask.Yield(context.cancellation);

                while (context.time < duration)
                {
                    if (!context.pause)
                    {
                        var deltaTime = context.unscaleTime ? Time.unscaledDeltaTime : Time.deltaTime;
                        context.time += deltaTime;
                        if (context.time > duration)
                        {
                            context.time = duration;
                        }
                        playableDirector.time = context.time;
                        playableDirector.Evaluate();
                    }
                    await UniTask.Yield(context.cancellation);
                }
            }
            finally
            {
                playableDirector.Stop();
            }
        }

        public static async UniTask RewindAsync(this PlayableDirector playableDirector, bool unscaleTime = false, bool startWithCurrent = false, CancellationToken cancellation = default)
        {
            var duration = playableDirector.duration;
            playableDirector.initialTime = -1;
            if (!startWithCurrent)
            {
                playableDirector.time = duration;
            }

            playableDirector.Evaluate();
            try
            {
                await UniTask.Yield(cancellation);

                while (playableDirector.time > 0)
                {
                    var deltaTime = unscaleTime ? Time.unscaledDeltaTime : Time.deltaTime;
                    playableDirector.time = playableDirector.time - deltaTime;
                    if (playableDirector.time < 0)
                    {
                        playableDirector.time = 0;
                    }

                    playableDirector.Evaluate();
                    await UniTask.Yield(cancellation);
                }
            }
            finally
            {
                playableDirector.Stop();
            }
        }


        public class TimelineContext
        {
            public double time;
            public bool pause;
            public bool unscaleTime;
            public bool startWithCurrent;
            public CancellationToken cancellation;

            public void Reset()
            {
            }
        }
    }
}